package liuyang.bigeventserver.modules.security.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import liuyang.bigeventserver.common.R;
import liuyang.bigeventserver.modules.security.entity.User;
import liuyang.bigeventserver.modules.security.jwt.JwtTokenService;
import liuyang.bigeventserver.modules.security.mapper.UserMapper;
import liuyang.bigeventserver.modules.security.service.DBUserDetailsServie;
import liuyang.bigeventserver.modules.security.vo.CachedInfo;
import liuyang.bigeventserver.modules.security.vo.SysUserDetails;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.URL;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 注：login利用用了Spring Security的过滤器。接收post提交参数的定制部分见JsonLoginAuthenticationFilter
 *
 * @author xconf
 * @since 2024/2/27
 */
@RestController
@RequestMapping("/user")
@AllArgsConstructor
@Slf4j
public class SysUserController {

    private final UserMapper userMapper;
    private final DBUserDetailsServie dbUserDetailsServie;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenService jwtTokenService;// 2024/3/3 更改密码后需要重新签发jwt，需要这个服务。
    private final RedisTemplate redisTemplate3;// 2024/3/3 更改密码后需要重新签发jwt，需要这个服务。

    @GetMapping("/list")
    public R getUserList() {// debug only
        final List<User> users = userMapper.selectList(new QueryWrapper<>());
        log.debug("users = {}", users);
        return R.ok().put("data", users);
    }

    @PostMapping("/add")
    public R add(@RequestBody  @Validated User user) {
        log.debug("param user = {}", user);

        if (dbUserDetailsServie.userExists(user.getUsername())) {
            return R.error("用户已存在");
        }

        dbUserDetailsServie.createUser(org.springframework.security.core.userdetails.User
                .withUsername(user.getUsername())
                .password(user.getPassword())
                .build());
        return R.ok("用户添加成功");
    }

    // 为配合x-www-form-urlencoded类型
    @PostMapping("/register")
    public R register(User user) {
        log.debug("param user = {}", user);

        /*if (dbUserDetailsServie.userExists(user.getUsername())) {
            return R.error("用户已存在");
        }

        dbUserDetailsServie.createUser(org.springframework.security.core.userdetails.User
                .withUsername(user.getUsername())
                .password(user.getPassword())
                .build());*/
        return R.ok("用户添加成功");
    }

    // 用户认证信息
    // https://www.bilibili.com/video/BV14b4y1A7Wz/?p=29&spm_id_from=pageDriver&vd_source=8bd7b24b38e3e12c558d839b352b32f4
    @GetMapping("/info")
    public R getCurrentUserInfo() {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        SysUserDetails sysUserDetails = (SysUserDetails) authentication.getPrincipal();
        return R.ok()
                .put("用户名(from authentication)", authentication.getName())
                .put("密码(from authentication)", authentication.getCredentials())
                .put("权限(from authentication)", authentication.getAuthorities())
                .put("用户名(principal)", sysUserDetails.getUsername())
                .put("用户id(principal)", sysUserDetails.getUserid());

        /*org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User)authentication.getPrincipal();
        return R.ok().put("authentication(有些字段被保护了)：", authentication)
                .put("用户名", user.getUsername())
                .put("密码", user.getPassword())// 拿不到
                .put("权限", user.getAuthorities());*/
    }

    // 用户更新
    /**
     * jsr303
     * https://www.bilibili.com/video/BV14z4y1N7pg/?p=24&spm_id_from=pageDriver&vd_source=8bd7b24b38e3e12c558d839b352b32f4
     * 分组校验：
     * https://www.bilibili.com/video/BV17i4y157Ah/?p=11&spm_id_from=pageDriver&vd_source=8bd7b24b38e3e12c558d839b352b32f4
     *
     * @param user
     * @return
     */
    @PostMapping("/update")
    public R updateUser(@RequestBody  @Validated(User.Update.class) User user) {// 考虑分组验证
    //public R updateUser(@RequestBody User user) {
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = Wrappers.<User>lambdaUpdate();
        userLambdaUpdateWrapper.eq(User::getUsername, user.getUsername());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.update(user, userLambdaUpdateWrapper);
        return R.ok("用户信息更新成功");
    }

    // 更新用户头像
    // https://www.bilibili.com/video/BV14z4y1N7pg/?p=25&spm_id_from=pageDriver&vd_source=8bd7b24b38e3e12c558d839b352b32f4
    @PatchMapping("/updateAvatar")
    public R updateAvatar(@RequestParam @URL String avatarUrl) {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        final String username = authentication.getName();

        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = Wrappers.<User>lambdaUpdate();
        userLambdaUpdateWrapper.eq(User::getUsername, username);
        userLambdaUpdateWrapper.set(User::getUserPic, avatarUrl);
        userLambdaUpdateWrapper.set(User::getUpdateTime, LocalDateTime.now());
        userMapper.update(userLambdaUpdateWrapper);

        return R.ok("头像更新成功");
    }

    // 更新用户密码
    // https://www.bilibili.com/video/BV14z4y1N7pg/?p=26&spm_id_from=pageDriver&vd_source=8bd7b24b38e3e12c558d839b352b32f4
    // 旧密码、新密码、确认新密码
    /**
     * {
     *     old_pwd:
     *     new_pwd:
     *     re_new_pwd:
     * }
     */
    @PatchMapping("/updatePwd")
    public R updatePwd(@RequestBody Map<String, String> params) {
        // 注意：项目上线需要屏蔽这句
        log.debug("params = {}", params);

        /*final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        final String username = authentication.getName();
        */
        final SysUserDetails sysUserDetails = (SysUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        final String username = sysUserDetails.getUsername();
        final Long userid = sysUserDetails.getUserid();

        // 1. 校验参数
        String oldPwd = params.get("old_pwd");
        String newPwd = params.get("new_pwd");
        String reNewPwd = params.get("re_new_pwd");
        if (!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd) || !StringUtils.hasLength(reNewPwd)) {
            return R.error("缺少必要参数").put("details:" ,"JSON需要包含三个字段：old_pwd, new_pwd, re_new_pwd，且不能为空。");
        }

        final LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.<User>lambdaQuery();
        userLambdaQueryWrapper.eq(User::getUsername, username);
        final User user = userMapper.selectOne(userLambdaQueryWrapper);

        // 比较新旧密码
        if (!passwordEncoder.matches(oldPwd, user.getPassword())) {
            return R.error("输入旧密码错误");
        }
        // 比较新密码
        if (!newPwd.equals(reNewPwd)) {
            return R.error("两次输入的新密码不一致");
        }

        // 2. 更新密码
        final LambdaUpdateWrapper<User> userLambdaUpdateWrapper = Wrappers.<User>lambdaUpdate();
        userLambdaUpdateWrapper.eq(User::getUsername, username);
        userLambdaUpdateWrapper.set(User::getPassword, passwordEncoder.encode(newPwd));
        userMapper.update(userLambdaUpdateWrapper);

        // 3. 签发新令牌并更新缓存 2024/3/3 add
        // https://www.bilibili.com/video/BV14z4y1N7pg/?p=39&spm_id_from=pageDriver&vd_source=8bd7b24b38e3e12c558d839b352b32f4
        final String jwtNew = jwtTokenService.sign(String.valueOf(userid), username);
        CachedInfo cachedInfo = new CachedInfo(jwtNew, sysUserDetails.getAuthorities());
        redisTemplate3.opsForValue().set(username, cachedInfo);// 删除旧令牌，新令牌生效。这样前端就需要重新登录。

        return R.ok("密码更新成功").put("token", jwtNew);
    }

}
